package org.example.toy;


import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

//熟悉nio的小玩具 非阻塞多路复用监听  的 服务端 暂不考虑粘包拆包处理
public class NioServerToy {
    public static void main(String[] args) throws Exception {

        //总思路： 阻塞点都用监听器（选择器）模式打通

        //先搞个监听器
        Selector selector = Selector.open();

        //服务器channel
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);//启用非阻塞模式
        ssc.bind(new InetSocketAddress("localhost", 8081));

        //监听服务器channel accept事件
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        //设置当事件触发的操作
        while (true) {
            //监听 阻塞等待时间触发
            selector.select();

            //此时有事件触发了 拿到
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                //拿到事件并移除
                SelectionKey key = iterator.next();
                iterator.remove();

                //根据触发的事件类型 做处理

                if (key.isAcceptable()) {
                    //accept事件
                    onAccept(selector, key);
                }
                if (key.isReadable()) {
                    //read事件
                    onRead(key);
                }

            }
        }




    }

    //处理读事件  拿数据真正的处理并返回 这里实现了极简的 拆包粘包处理
    private static void onRead(SelectionKey key) throws Exception {
        System.out.println("触发了read事件");

        SocketChannel channel = (SocketChannel) key.channel();

        //拿key的数据附件 如果有则顺着往后读即可 没有就新来一个 一个缓冲区一个完整信息
        ByteBuffer buffer = (ByteBuffer) key.attachment();

        if (buffer == null) {
            buffer = ByteBuffer.allocate(1024); // 自定义缓冲大小
            key.attach(buffer);
        }

        int bytesRead = channel.read(buffer);
        //-1表示客户端断开连接
        if (bytesRead == -1) {
            channel.close();
            return;
        }

        // 切换读模式查看数据
        buffer.flip();

        // 假设我们用换行符作为消息结束符，检查是否有完整消息
        byte[] data = new byte[buffer.limit()];
        buffer.get(data);
        String msg = new String(data);
        System.out.println("收到消息：" + msg);

        int index = msg.indexOf('*');
        if (index!=-1) {
            // 检测到* 把*之前的拆开即为一次完整消息 然后*后面的放回去
            System.out.println("完整消息：" +msg.substring(0,index) );

            // 处理完消息后清空buffer，准备下次读
            buffer.clear();

            buffer.put(msg.substring(index+1).getBytes());

            // 回复客户端
            channel.write(ByteBuffer.wrap("hello\n".getBytes()));
        } else {

            // 未读完整消息，攒着下次读
            buffer.position(buffer.limit());//移动下次读取的起始位置
            buffer.limit(buffer.capacity());
        }


    }

    //处理accept事件 主要集就是小channel注册到监听器上
    private static void onAccept(Selector selector, SelectionKey key) throws Exception {

        //根据这个key拿到对应的channel
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

        //小channel注册
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        //转成读事件的监听
        socketChannel.register(selector, SelectionKey.OP_READ);//读事件
    }


}
